package org.example.readwritelock;

import com.sun.javafx.scene.control.SizeLimitedList;
import lombok.extern.slf4j.Slf4j;
import org.example.utils.Sleeper;

import java.util.concurrent.locks.StampedLock;

/**
 * 测试 StampedLock的性能
 * @author ZhengDp
 * @Date 2023/2/13 23:13
 */
public class TestStampedLock {
    public static void main(String[] args) {
        //test1();
        test2();
    }

    /**
     * 写操作，乐观读锁升级
     */
    private static void test2(){
        DataContainerStamped dataContainer = new DataContainerStamped(1);
        new Thread(() -> {
            dataContainer.read(1000);
        },"t1").start();
        Sleeper.sleep(500);
        new Thread(() -> {
            dataContainer.write(1000);
        },"t2").start();
    }

    /**
     * 测试，乐观读锁之间可以并发，不会进行真正的加锁操作
     */
    private static void test1(){
        DataContainerStamped dataContainer = new DataContainerStamped(1);
        new Thread(() -> {
            dataContainer.read(1000);
        },"t3").start();
        Sleeper.sleep(500);
        new Thread(() -> {
            dataContainer.read(0);
        },"t4").start();
    }
}

@Slf4j(topic = "c.DataContainerStamped")
class DataContainerStamped{
    private int data;
    private final StampedLock lock = new StampedLock();

    public DataContainerStamped(int data) {
        this.data = data;
    }

    public int read( int readTime ) {
        long stamp = lock.tryOptimisticRead();
        log.info("optimistic read locking ... {}",stamp);
        Sleeper.sleep(readTime);
        if(lock.validate(stamp)) {
            log.info("read finish ... {}",stamp);
            return data;
        }
        log.info("updating to read lock ...{}",stamp);
        try {
            // 锁升级
            stamp = lock.readLock();
            log.info("read lock {}" , stamp);
            Sleeper.sleep(readTime);
            log.info("read unlock {}" , stamp);
            return data;
        }finally {
            log.info("read unlock {}" , stamp);
            lock.unlockRead(stamp);
        }
    }

    public void write(int newData) {
        long stamp = lock.writeLock();
        log.info("write lock {}" , stamp);
        try {
            Sleeper.sleep(2000);
            this.data = newData;
        }finally {
            log.info("write unlock {}",stamp);
            lock.unlockWrite(stamp);
        }
    }
}
